ddns-scripts: (cloudflare) add support for custom zone ID and DNS record ID
authorEric Leung <[email protected]>
Tue, 8 Jul 2025 10:01:44 +0000 (18:01 +0800)
committerFlorian Eckert <[email protected]>
Wed, 9 Jul 2025 07:48:00 +0000 (09:48 +0200)
User can (optionally) set zone_id and dns_record_id in param_opt.
This allows round robin DNS setup in Cloudflare.

Signed-off-by: Eric Leung <[email protected]>
net/ddns-scripts/Makefile
net/ddns-scripts/files/usr/lib/ddns/update_cloudflare_com_v4.sh

index ae4c448427684e2bf688d22a36040bf5033240d5..9c852cd81955ebb2370d49336a24014e4964ae0a 100644 (file)
@@ -8,7 +8,7 @@ include $(TOPDIR)/rules.mk
 
 PKG_NAME:=ddns-scripts
 PKG_VERSION:=2.8.2
-PKG_RELEASE:=74
+PKG_RELEASE:=75
 
 PKG_LICENSE:=GPL-2.0
 
index 28861c7c6e80a72b8d26ec42dfbce2ef9070f89d..8212931953e4afb9919cefe8d0a9f3dceadf11b6 100644 (file)
 # option username  - your cloudflare e-mail
 # option password  - cloudflare api key, you can get it from cloudflare.com/my-account/
 # option domain    - "[email protected]"
+# option param_opt - (Optional) key=value pairs that are separated by space
+#                    if duplicate keys found, only the last occurrence will be used
+#                    example: "zone_id=123456789 dns_record_id=987654321"
+#                    current supported keys:
+#                    1. zone_id (API: GET https://api.cloudflare.com/client/v4/zones)
+#                       note: zone_id must be specified if dns_record_id is specified
+#                    2. dns_record_id (API: GET https://api.cloudflare.com/client/v4/zones/{zone_id}/dns_records)
 #
 # The proxy status would not be changed by this script. Please change it in Cloudflare dashboard manually.
 #
 [ -z "$password" ] && write_log 14 "Service section not configured correctly! Missing secret as 'password'"
 [ $use_https -eq 0 ] && use_https=1    # force HTTPS
 
+# parse param_opt
+if [ -n "$param_opt" ]; then
+       for pair in $param_opt; do
+               case $pair in
+               zone_id=*)
+                       zone_id=${pair#*=}
+                       write_log 7 "User defined zone_id: $zone_id"
+                       ;;
+               dns_record_id=*)
+                       dns_record_id=${pair#*=}
+                       write_log 7 "User defined dns_record_id: $dns_record_id"
+                       ;;
+               *)
+                       # Ignore others
+                       ;;
+               esac
+       done
+fi
+
+# If dns_record_id is specified, zone_id must also be specified
+if [ -n "$dns_record_id" ] && [ -z "$zone_id" ]; then
+       write_log 4 "zone_id cannot be empty if dns_record_id is specified"
+       return 127
+fi
+
 # used variables
 local __HOST __DOMAIN __TYPE __URLBASE __PRGBASE __RUNPROG __DATA __IPV6 __ZONEID __RECID
 local __URLBASE="https://api.cloudflare.com/client/v4"
@@ -140,14 +172,20 @@ else
 fi
 __PRGBASE="$__PRGBASE --header 'Content-Type: application/json' "
 
+# read zone id for registered domain.TLD
+__RUNPROG="$__PRGBASE --request GET '$__URLBASE/zones?name=$__DOMAIN'"
+cloudflare_transfer || return 1
+
 if [ -n "$zone_id" ]; then
-       __ZONEID="$zone_id"
+       # validate user defined zone id
+       __ZONEID=$(jsonfilter -i $DATFILE -e "@.result[@.id='$zone_id'].id")
+       [ -z "$__ZONEID" ] && {
+               write_log 4 "Invalid user defined zone_id for domain.tld: '$__DOMAIN'"
+               return 127
+       }
 else
-       # read zone id for registered domain.TLD
-       __RUNPROG="$__PRGBASE --request GET '$__URLBASE/zones?name=$__DOMAIN'"
-       cloudflare_transfer || return 1
        # extract zone id
-       __ZONEID=$(grep -o '"id":\s*"[^"]*' $DATFILE | grep -o '[^"]*$' | head -1)
+       __ZONEID=$(jsonfilter -i $DATFILE -e "@.result.*.id" | head -1)
        [ -z "$__ZONEID" ] && {
                write_log 4 "Could not detect 'zone id' for domain.tld: '$__DOMAIN'"
                return 127
@@ -157,12 +195,29 @@ fi
 # read record id for A or AAAA record of host.domain.TLD
 __RUNPROG="$__PRGBASE --request GET '$__URLBASE/zones/$__ZONEID/dns_records?name=$__HOST&type=$__TYPE'"
 cloudflare_transfer || return 1
-# extract record id
-__RECID=$(grep -o '"id":\s*"[^"]*' $DATFILE | grep -o '[^"]*$' | head -1)
-[ -z "$__RECID" ] && {
-       write_log 4 "Could not detect 'record id' for host.domain.tld: '$__HOST'"
-       return 127
-}
+
+if [ -n "$dns_record_id" ]; then
+       # validate user defined record id
+       __RECID=$(jsonfilter -i $DATFILE -e "@.result[@.id='$dns_record_id'].id")
+       [ -z "$__RECID" ] && {
+               write_log 4 "Invalid user defined dns_record_id for host.domain.tld: '$__HOST'"
+               return 127
+       }
+else
+       # extract record id
+       __RECID=$(jsonfilter -i $DATFILE -e "@.result.*.id" | head -1)
+       [ -z "$__RECID" ] && {
+               write_log 4 "Could not detect 'record id' for host.domain.tld: '$__HOST'"
+               return 127
+       }
+fi
+
+# If dns_record_id is specified, grab the stored IP for that specific record
+# So that the IP checking behavior below works even for domains with multiple IPs
+if [ -n "$dns_record_id" ]; then
+       __RUNPROG="$__PRGBASE --request GET '$__URLBASE/zones/$__ZONEID/dns_records/$__RECID'"
+       cloudflare_transfer || return 1
+fi
 
 # extract current stored IP
 __DATA=$(grep -o '"content":\s*"[^"]*' $DATFILE | grep -o '[^"]*$' | head -1)